package cn.buk.tms.flight.dto.response;

import cn.buk.tms.common.dto.CustomerDto;
import cn.buk.tms.common.dto.PaymentMethodDto;
import cn.buk.tms.common.dto.SupplierDto;
import cn.buk.tms.common.dto.base.BaseDto;
import cn.buk.tms.common.dto.base.BaseOrderDto;
import cn.buk.tms.common.dto.base.BasePriceDto;
import cn.buk.tms.common.dto.flight.BaseFlightOrderFlightDto;
import cn.buk.tms.common.dto.flight.BaseFlightOrderPassengerDto;
import cn.buk.tms.common.dto.flight.BaseFlightOrderResponseDto;
import cn.buk.tms.dto.AttachmentDto;
import cn.buk.tms.dto.SmsDetailDto;
import cn.buk.common.util.DateUtil;
import cn.buk.tms.entity.misc.Customer;
import cn.buk.tms.entity.misc.FlightInfo;
import cn.buk.tms.entity.order.flight.*;
import cn.buk.tms.tms.response.DeliveryNoteResponseDto;
import cn.buk.tms.vas.dto.response.VasOrderResponseDto;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.beans.BeanUtils;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static cn.buk.common.Constant.PSG_TYPE_ADU;
import static cn.buk.common.Constant.PSG_TYPE_CHD;
import static cn.buk.tms.common.constant.TmsOrderConstant.*;

/**
 * cn.buk.tms 表示广义的差旅系统
 * cn.buk.tms.tms 表示狭义的差旅系统，特指后台部分
 * cn.buk.tms.eotms 表示狭义的差旅系统，特指前台（给客户自助使用）的部分
 *
 * cn.buk.tms.tms.response 包含供页面使用的DTO类
 */
public class FlightOrderResponseDto extends BaseFlightOrderResponseDto {

  /**
   * 外部订单号
   */
  private String externalOrderNo;

  private String linkMobile;

  private String linkEmail;

  private String bigPnrNo;

  /**
   * 开票提交时间
   */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
  private Date submitTime;

  private List<AttachmentDto> attachments;

  public static FlightOrderResponseDto create(int errcode, String errmsg) {
    FlightOrderResponseDto dto = new FlightOrderResponseDto();
    dto.setErrcode(errcode);
    dto.setErrmsg(errmsg);
    return dto;
  }

  /**
   * 机票订单的使用状态
   */
  private int showStatus;

  /**
   * 乘机人信息
   */
  private List<BaseFlightOrderPassengerDto> passengers;

  /**
   * 票号信息
   */
  private List<FlightOrderTicketDto> tickets;

  /**
   * 航班信息
   */
  private List<BaseFlightOrderFlightDto> flights;

  /**
   * 子订单
   */
  private List<BaseOrderDto> subOrders;

  /**
   * 与订单相关联的短信
   */
  private List<SmsDetailDto> smsDetails;

  /**
   * 与订单相关联的退票单
   */
  private List<RefundOrderResponseDto> refunds;

  /**
   * 与订单相关联的改签单
   */
  private List<ChangeOrderResponseDto> changes;

  /**
   * 与订单相关联的服务单
   */
  private List<VasOrderResponseDto> services;

  /**
   * 相关的配送单
   */
  private List<DeliveryNoteResponseDto> deliveries;

  /**
   * @Deprecated  无用？
   *
   */
  @Deprecated
  private List<BaseDto> extras;

  private FlightOrderCancelRequest cancelRequest;

  public static FlightOrderResponseDto createByFlightOrder(final FlightOrder o) {
    FlightOrderResponseDto dto = new FlightOrderResponseDto();
    dto.setErrcode(0);

    BeanUtils.copyProperties(o, dto, "flights", "passengers", "extras", "tickets");

    //订单状态
    dto.setOrderStatus(o.getStatus());
    //邮寄地址
    dto.setAddress(o.getItineraryMailingAddress());
    //价格校验状态
    dto.setPriceCheckedStatus(o.getPriceChecked());
    dto.setPriceCheckedTime(o.getPriceCheckedTime());
    //审批相关信息
    if (o.getApprovalInfo() != null) {
      dto.setSpecifiedApprover(o.getApprovalInfo().getSpecifiedApprover());
      dto.setApprovalStatus(o.getApprovalInfo().getApprovalStatus());
      dto.setApprovalDenyReason(o.getApprovalInfo().getApprovalDenyReason());
      dto.setViolationStatus(o.getApprovalInfo().getViolationStatus());
      dto.setViolationReason(o.getApprovalInfo().getViolationReason());
    }

    if (dto.getCustomerType() == 1) {
      Customer c = o.getCustomer();
      dto.setCustomer(CustomerDto.create(c.getId(), c.getVipName(), c.getCustomerCode()));
    }

    if (o.getSupplier() != null) {
      SupplierDto supplierDto = new SupplierDto();
      supplierDto.setId(o.getSupplier().getId());
      supplierDto.setName(o.getSupplier().getName());
      supplierDto.setIsBsp(o.getSupplier().getIsBsp());
      dto.setSupplier(supplierDto);
    }

    if (o.getPaymentMethod() != null) {
      PaymentMethodDto methodDto = new PaymentMethodDto();
      methodDto.setId(o.getPaymentMethod().getId());
      methodDto.setName(o.getPaymentMethod().getName());
      dto.setPaymentMethod(methodDto);
    }

    for (FlightOrderPassenger psg : o.getPassengers()) {
      BaseFlightOrderPassengerDto psgDto = new BaseFlightOrderPassengerDto();
      BeanUtils.copyProperties(psg, psgDto, "ticketNos");

      for(String ticketNo: psg.getTicketNos()) {
        psgDto.getTicketNos().add(ticketNo);
      }

      dto.getPassengers().add(psgDto);
    }

    dto.calcPsgCount();

    // 成人部分的价格
    if (dto.getAdultCount() > 0) {
      processAdultPrice(dto, o);
    }

    // 儿童部分的价格
    if (dto.getChildCount() > 0) {
      processChildPrice(dto, o);
    }

    // 婴儿部分的价格
    if (dto.getInfantCount() > 0) {
      processInfantPrice(dto, o);
    }



    for (FlightOrderFlight flt0 : o.getFlights()) {
      processFlightInfo(dto, flt0);
    }

    for(FlightOrderTicket ticket: o.getTickets()) {
      FlightOrderTicketDto ticketDto = new FlightOrderTicketDto();
      dto.getTickets().add(ticketDto);

      BeanUtils.copyProperties(ticket, ticketDto);

      for(FlightOrderPassenger psg: o.getPassengers()) {
        if (psg.getId() == ticket.getPassengerId()) {
          ticketDto.setPassengerName(psg.getName());
          break;
        }
      }
    }

    //将客票航段的状态反馈到乘机人的ticketStatus
    dto.processPassengerTicketStatus();

    dto.setSubTotal(dto.getTotalAmount());

    return dto;
  }

  /**
   * 计算乘机人类型的数量
   */
  private void calcPsgCount() {
    int adultCount = 0;
    int childCount = 0;
    int infantCount = 0;
    for(var psg: this.getPassengers()) {
      if (psg.getPsgType() == null || psg.getPsgType() == PSG_TYPE_ADU) {
        adultCount++;
      } else if (psg.getPsgType() == PSG_TYPE_CHD) {
        childCount++;
      } else {
        infantCount++;
      }
    }

    this.setAdultCount(adultCount);
    this.setChildCount(childCount);
    this.setInfantCount(infantCount);
  }

  private static void processFlightInfo(FlightOrderResponseDto dto, FlightOrderFlight flt0) {
    BaseFlightOrderFlightDto fltDto = new BaseFlightOrderFlightDto();
    fltDto.setSegOrder(flt0.getSegOrder());

    FlightInfo f0 = flt0.getFlight();
    fltDto.setDport(f0.getDport());
    fltDto.setAport(f0.getAport());
    fltDto.setFlightNo(f0.getFlightNo());
    try {
      if (f0.getDdate().length() > 0) {
        fltDto.setDdate(DateUtil.convertToDate(f0.getDdate(), "yyyy-MM-dd"));
      }
    } catch (ParseException ignored) {
    }
    fltDto.setSubclass(f0.getSubclass());
    fltDto.setCabinClass(f0.getCabinClass());

    fltDto.setDportName(f0.getDepartureAirportName());
    fltDto.setAportName(f0.getArrivalAirportName());
    fltDto.setDtime(f0.getDtime());
    fltDto.setAtime(f0.getAtime());
    fltDto.setDterm(f0.getDterm());
    fltDto.setAterm(f0.getAterm());

    fltDto.setChangeRule(flt0.getChangeRule());
    fltDto.setRefundRule(flt0.getRefundRule());

    dto.getFlights().add(fltDto);

  }

  private static void processInfantPrice(FlightOrderResponseDto dto, FlightOrder o) {
    BasePriceDto priceDto = new BasePriceDto();
    dto.getPrices().add(priceDto);
    priceDto.setPriceType(2);
    priceDto.setPrice(o.getInfPrice());
    priceDto.setParValue(o.getInfParvalue());
    priceDto.setTax(o.getInfTax());
    priceDto.setInsurance(o.getInfInsurance());
    priceDto.setServiceCharge(o.getInfServiceCharge());
    priceDto.setDiscount(o.getInfDiscount());
    priceDto.setCommission(o.getInfCommission());
    priceDto.setCommission2(o.getInfCommission2());
    priceDto.setCost(o.getInfCost());

    priceDto.calc();
  }

  private static void processChildPrice(FlightOrderResponseDto dto, FlightOrder o) {
    BasePriceDto priceDto = new BasePriceDto();
    dto.getPrices().add(priceDto);
    priceDto.setPriceType(1);
    priceDto.setPrice(o.getChdPrice());
    priceDto.setParValue(o.getChdParvalue());
    priceDto.setTax(o.getChdTax());
    priceDto.setInsurance(o.getChdInsurance());
    priceDto.setServiceCharge(o.getChdServiceCharge());
    priceDto.setDiscount(o.getChdDiscount());
    priceDto.setCommission(o.getChdCommission());
    priceDto.setCommission2(o.getChdCommission2());
    priceDto.setCost(o.getChdCost());

    priceDto.calc();
  }

  private static void processAdultPrice(FlightOrderResponseDto dto, final FlightOrder o) {
    BasePriceDto priceDto = new BasePriceDto();
    dto.getPrices().add(priceDto);
    priceDto.setPriceType(0);
    priceDto.setPrice(o.getPrice());
    priceDto.setParValue(o.getParvalue());
    priceDto.setTax(o.getTax());
    priceDto.setInsurance(o.getInsurance());
    priceDto.setServiceCharge(o.getServiceCharge());
    priceDto.setDiscount(o.getDiscount());
    priceDto.setCommission(o.getCommission());
    priceDto.setCommission2(o.getCommission2());
    priceDto.setCost(o.getCost());

    priceDto.calc();
  }

  public void processPassengerTicketStatus() {
    for(BaseFlightOrderPassengerDto psg: this.getPassengers()) {
      int ticketStatus = -1;
      for(FlightOrderTicketDto ticket: this.getTickets()) {
        if (!ticket.getPassengerName().equalsIgnoreCase(psg.getName())) {
          continue;
        }

        if (ticket.getTicketStatus() == null) {
          ticketStatus = -1;
          break;
        }

        if (ticket.getTicketStatus() == 1
                || ticket.getTicketStatus() == 2
                || ticket.getTicketStatus() == 3
                || ticket.getTicketStatus() == 4
                || ticket.getTicketStatus() == 128) {
          ticketStatus = 1;
        } else  {
          ticketStatus = -1;
          break;
        }
      }

      if (ticketStatus >= 0) {
        psg.setTicketStatus(ticketStatus);
      }
    }

  }

  public String getShowStatusDesc() {
    switch (this.showStatus) {
      case FLIGHT_ORDER_TICKET_WHOLE_USED:
        return "已使用";
      case FLIGHT_ORDER_TICKET_WHOLE_REFUNDED:
        return "已退票";
      case FLIGHT_ORDER_TICKET_WHOLE_EXCHANGED:
        return "已改签";
      case FLIGHT_ORDER_TICKET_PARTIAL_USED
              + FLIGHT_ORDER_TICKET_PARTIAL_EXCHANGED:
        return "部分使用改签";
      case FLIGHT_ORDER_TICKET_PARTIAL_USED
              + FLIGHT_ORDER_TICKET_PARTIAL_REFUNDED:
        return "部分使用退票";
      case FLIGHT_ORDER_TICKET_PARTIAL_EXCHANGED
              + FLIGHT_ORDER_TICKET_PARTIAL_REFUNDED:
        return "部分改签退票";
      case FLIGHT_ORDER_TICKET_PARTIAL_USED
              + FLIGHT_ORDER_TICKET_PARTIAL_EXCHANGED
              + FLIGHT_ORDER_TICKET_PARTIAL_REFUNDED:
        return "部分使用改签退票";
      default:
        return "";
    }
  }

  private int enterpriseId;

  /**
   * 出票人
   */
  private String ticketer;

  /**
   * 收款人
   */
  private String cashier;

  /**
   * 人数
   */
  private int psgCount;

  /**
   * 订单描述
   */
  private String orderDesc;

  /**
   * 需要在此时间之前出票
   * ticketing time limit
   */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
  private Date tktl;

  /**
   * 订单中的编码使用到的大客户号
   */
  private String keyCustomerNo;

  /**
   * 自动出票的状态
   * 0 / null: 不自动出票
   * 1: 等待自动出票
   * 2: 自动出票中，等待用户确认出票金额（PAT:A出现多个价格的情况下）
   * 8: 自动出票完成
   */
  private Integer autoTicketing;

  /**
   * 自动出票时需要选择的金额，为空或大于0的
   */
  private Integer autoTicketingPrice;

  /**
   * 账单导入是否成功
   * null / 0 - 默认状态, 未导入
   * 1 - 导入成功
   */
  private Integer billImported;

  /**
   * 出票日期
   */
  @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
  private Date etdzDate;

  @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
  private Date deliveryDate;

  /**
   * 打印次数
   */
  private Integer itineraryPrintCount;

  /**
   * 收件人
   * 以后移到配送单中
   */
  private String itineraryRecipient;

  /**
   * 收件人联系电话
   * 以后移到配送单中
   */
  private String itineraryRecipientMobile;

  /**
   * 当itineraryType不为0时，此表示邮寄地址
   * 以后移到配送单中
   */
  private String itineraryMailingAddress;

  /**
   * 当itineraryType不为0时，邮寄状态
   * 0 - 未邮寄
   * 1 - 已邮寄
   * 以后移到配送单中
   */
  private Integer itineraryMailingStatus;

  /**
   * 当itineraryType不为0时，此表示备注
   * 以后移到配送单中
   */
  private String itineraryMailingMemo;

  private SupplierDto supplier;

  private PaymentMethodDto paymentMethod;

  /**
   * 总成本
   */
  private Double totalCost;

  /**
   * 此订单的总利润
   */
  private Double profit;

  /**
   * 通知类型
   */
  private int noticeType;

  /**
   * 通知信息内容
   */
  private String noticeContent;

  /**
   * 通知信息是否确认阅读了
   * 0 - 未读
   * 1 - 已读
   */
  private int noticeConfirmed;

  /**
   * 通知发送的时间
   */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
  private Date noticeTime;

  /**
   * DETR的更新时间
   */
  private Date detrRefreshTime;;

  /**
   * 订单状态, 用于兼容原来的用法
   */
  public int getStatus() {
    return this.getOrderStatus();
  }

  public int getEnterpriseId() {
    return enterpriseId;
  }

  public void setEnterpriseId(int enterpriseId) {
    this.enterpriseId = enterpriseId;
  }

  public String getTicketer() {
    return ticketer;
  }

  public void setTicketer(String ticketer) {
    this.ticketer = ticketer;
  }

  public String getCashier() {
    return cashier;
  }

  public void setCashier(String cashier) {
    this.cashier = cashier;
  }

  public int getPsgCount() {
    return psgCount;
  }

  public void setPsgCount(int psgCount) {
    this.psgCount = psgCount;
  }

  public String getOrderDesc() {
    return orderDesc;
  }

  public void setOrderDesc(String orderDesc) {
    this.orderDesc = orderDesc;
  }

  public List<BaseFlightOrderPassengerDto> getPassengers() {
    if (passengers == null) {
      passengers = new ArrayList<>();
    }
    return passengers;
  }

  public void setPassengers(List<BaseFlightOrderPassengerDto> passengers) {
    this.passengers = passengers;
  }

  public List<BaseFlightOrderFlightDto> getFlights() {
    if (flights == null) {
      flights = new ArrayList<>();
    }
    return flights;
  }

  public void setFlights(List<BaseFlightOrderFlightDto> flights) {
    this.flights = flights;
  }

  public Date getTktl() {
    return tktl;
  }

  public void setTktl(Date tktl) {
    this.tktl = tktl;
  }

  public String getKeyCustomerNo() {
    return keyCustomerNo;
  }

  public void setKeyCustomerNo(String keyCustomerNo) {
    this.keyCustomerNo = keyCustomerNo;
  }

  public Integer getAutoTicketing() {
    return autoTicketing;
  }

  public void setAutoTicketing(Integer autoTicketing) {
    this.autoTicketing = autoTicketing;
  }

  public Integer getAutoTicketingPrice() {
    return autoTicketingPrice;
  }

  public void setAutoTicketingPrice(Integer autoTicketingPrice) {
    this.autoTicketingPrice = autoTicketingPrice;
  }

  public Integer getBillImported() {
    return billImported;
  }

  public void setBillImported(Integer billImported) {
    this.billImported = billImported;
  }

  public Date getEtdzDate() {
    return etdzDate;
  }

  public void setEtdzDate(Date etdzDate) {
    this.etdzDate = etdzDate;
  }

  public Date getDeliveryDate() {
    return deliveryDate;
  }

  public void setDeliveryDate(Date deliveryDate) {
    this.deliveryDate = deliveryDate;
  }

  public Integer getItineraryPrintCount() {
    return itineraryPrintCount;
  }

  public void setItineraryPrintCount(Integer itineraryPrintCount) {
    this.itineraryPrintCount = itineraryPrintCount;
  }

  public String getItineraryRecipient() {
    return itineraryRecipient;
  }

  public void setItineraryRecipient(String itineraryRecipient) {
    this.itineraryRecipient = itineraryRecipient;
  }

  public String getItineraryRecipientMobile() {
    return itineraryRecipientMobile;
  }

  public void setItineraryRecipientMobile(String itineraryRecipientMobile) {
    this.itineraryRecipientMobile = itineraryRecipientMobile;
  }

  public String getItineraryMailingAddress() {
    return itineraryMailingAddress;
  }

  public void setItineraryMailingAddress(String itineraryMailingAddress) {
    this.itineraryMailingAddress = itineraryMailingAddress;
  }

  public Integer getItineraryMailingStatus() {
    return itineraryMailingStatus;
  }

  public void setItineraryMailingStatus(Integer itineraryMailingStatus) {
    this.itineraryMailingStatus = itineraryMailingStatus;
  }

  public String getItineraryMailingMemo() {
    return itineraryMailingMemo;
  }

  public void setItineraryMailingMemo(String itineraryMailingMemo) {
    this.itineraryMailingMemo = itineraryMailingMemo;
  }

  public SupplierDto getSupplier() {
    return supplier;
  }

  public void setSupplier(SupplierDto supplier) {
    this.supplier = supplier;
  }

  public PaymentMethodDto getPaymentMethod() {
    return paymentMethod;
  }

  public void setPaymentMethod(PaymentMethodDto paymentMethod) {
    this.paymentMethod = paymentMethod;
  }

  public Double getProfit() {
    return profit;
  }

  public void setProfit(Double profit) {
    this.profit = profit;
  }

  public List<SmsDetailDto> getSmsDetails() {
    if (smsDetails == null) {
      smsDetails = new ArrayList<>();
    }
    return smsDetails;
  }

  public void setSmsDetails(List<SmsDetailDto> smsDetails) {
    this.smsDetails = smsDetails;
  }

  public List<RefundOrderResponseDto> getRefunds() {
    if(refunds == null) {
      refunds = new ArrayList<>();
    }
    return refunds;
  }

  public void setRefunds(List<RefundOrderResponseDto> refunds) {
    this.refunds = refunds;
  }

  public List<ChangeOrderResponseDto> getChanges() {
    if (changes == null) {
      changes = new ArrayList<>();
    }
    return changes;
  }

  public void setChanges(List<ChangeOrderResponseDto> changes) {
    this.changes = changes;
  }

  public List<VasOrderResponseDto> getServices() {
    if (this.services == null) {
      this.services = new ArrayList<>();
    }
    return services;
  }

  public void setServices(List<VasOrderResponseDto> services) {
    this.services = services;
  }

  public List<DeliveryNoteResponseDto> getDeliveries() {
    if (deliveries == null) {
      deliveries = new ArrayList<>();
    }
    return deliveries;
  }

  public void setDeliveries(List<DeliveryNoteResponseDto> deliveries) {
    this.deliveries = deliveries;
  }

  public FlightOrderCancelRequest getCancelRequest() {
    return cancelRequest;
  }

  public void setCancelRequest(FlightOrderCancelRequest cancelRequest) {
    this.cancelRequest = cancelRequest;
  }

  public List<BaseDto> getExtras() {
    if (extras == null) {
      extras = new ArrayList<>();
    }
    return extras;
  }

  public void setExtras(List<BaseDto> extras) {
    this.extras = extras;
  }


  public Double getTotalCost() {
    return totalCost;
  }

  public void setTotalCost(Double totalCost) {
    this.totalCost = totalCost;
  }

  public List<BaseOrderDto> getSubOrders() {
    if (subOrders == null) {
      subOrders = new ArrayList<>();
    }
    return subOrders;
  }

  public void setSubOrders(List<BaseOrderDto> subOrders) {
    this.subOrders = subOrders;
  }

  public BasePriceDto getAdultPrice() {
    for(BasePriceDto priceDto: this.getPrices()) {
      if (priceDto.getPriceType() == 0) {
        return priceDto;
      }
    }
    return null;
  }

  public List<FlightOrderTicketDto> getTickets() {
    if (tickets == null) {
      tickets = new ArrayList<>();
    }
    return tickets;
  }

  public void setTickets(List<FlightOrderTicketDto> tickets) {
    this.tickets = tickets;
  }

  public int getNoticeType() {
    return noticeType;
  }

  public void setNoticeType(int noticeType) {
    this.noticeType = noticeType;
  }

  public String getNoticeContent() {
    return noticeContent;
  }

  public void setNoticeContent(String noticeContent) {
    this.noticeContent = noticeContent;
  }

  public int getNoticeConfirmed() {
    return noticeConfirmed;
  }

  public void setNoticeConfirmed(int noticeConfirmed) {
    this.noticeConfirmed = noticeConfirmed;
  }

  public Date getNoticeTime() {
    return noticeTime;
  }

  public void setNoticeTime(Date noticeTime) {
    this.noticeTime = noticeTime;
  }

  public int getShowStatus() {
    return showStatus;
  }

  public void setShowStatus(int showStatus) {
    this.showStatus = showStatus;
  }

  public List<AttachmentDto> getAttachments() {
    if (attachments == null) {
      attachments = new ArrayList<>();
    }
    return attachments;
  }



  public Date getDetrRefreshTime() {
    return detrRefreshTime;
  }

  public void setDetrRefreshTime(Date detrRefreshTime) {
    this.detrRefreshTime = detrRefreshTime;
  }

  public String getExternalOrderNo() {
    return externalOrderNo;
  }

  public void setExternalOrderNo(String externalOrderNo) {
    this.externalOrderNo = externalOrderNo;
  }

  public String getLinkMobile() {
    return linkMobile;
  }

  public void setLinkMobile(String linkMobile) {
    this.linkMobile = linkMobile;
  }

  public String getLinkEmail() {
    return linkEmail;
  }

  public void setLinkEmail(String linkEmail) {
    this.linkEmail = linkEmail;
  }

  public String getBigPnrNo() {
    return bigPnrNo;
  }

  public void setBigPnrNo(String bigPnrNo) {
    this.bigPnrNo = bigPnrNo;
  }

  public Date getSubmitTime() {
    return submitTime;
  }

  public void setSubmitTime(Date submitTime) {
    this.submitTime = submitTime;
  }
}
